#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "PrintManager.h"

#include "Class.h"
#include "OrdColl.h"
#include "PrintPort.h"
#include "FileDialog.h"
#include "ObjGridView.h"
#include "Fields.h"
#include "BorderItems.h"
#include "Buttons.h"
#include "Printer.h"
#include "ClassManager.h"
#include "Box.h"
#include "String.h"
#include "Scroller.h"
#include "Env.h"
#include "ET_stdio.h"

const int cNameMinWidth= 250;

bool gPrinting;
SmartPrintManager gPrintManager;

PrintManager *SmartPrintManager::MakePrintManager()
{
	if (pm == 0)
		pm= new PrintManager;
	return pm;
}

//---- PrintDialog -------------------------------------------------------------

class PrintDialog : public Dialog {
public:
	FileDialog *saveDialog;
	CompositeVObject *scroller;
	ObjGridView *collview;
	ToggleButton *options;
	IntField *from, *to;
	char *title;
	TextItem *no;
	Box *clu;

public:
	MetaDef(PrintDialog);

	PrintDialog(char *title= "Print");
	~PrintDialog();
	VObject *DoMakeContent();
	void Control(int id, int, void *v);
	void UpdateButtons();

	void StoreSettings(PrinterSettings*);
	void DoSetDefaults();
	void DoSetup();
};

//---- PrinterSettings --------------------------------------------------------

NewMetaImpl(PrinterSettings,Object, (T(paperSize), T(topLeftMargin),
			T(bottomRightMargin), TS(name), TB(showgrid)));

PrinterSettings::PrinterSettings(Point ps, Point tlm, Point brm, Rectangle)
{
	paperSize= ps;
	topLeftMargin= tlm;
	bottomRightMargin= brm;
	showgrid= FALSE;
}

PrinterSettings::~PrinterSettings()
{
}

Printer *PrinterSettings::GetPrinter()
{
	return gPrintManager->FindPrinter(name);
}

Rectangle PrinterSettings::GetPrintRect()
{
	return Rectangle(paperSize).Inset(10);
}

Rectangle PrinterSettings::GetViewRect()
{
	return Rectangle(topLeftMargin, paperSize - (topLeftMargin+bottomRightMargin));
}

void PrinterSettings::SetPaperSize(Point e)
{
	paperSize= e;
}

void PrinterSettings::SetTopLeftMargin(Point tlm)
{
	topLeftMargin= tlm;
}

void PrinterSettings::SetBottomRightMargin(Point brm)
{
	bottomRightMargin= brm;
}

void PrinterSettings::ShowPageBreaks(bool mode)
{
	showgrid= mode;
}

OStream& PrinterSettings::PrintOn(OStream &s)
{
	Object::PrintOn(s);
	return s << paperSize SP << topLeftMargin SP << bottomRightMargin SP
													<< name SP << showgrid SP;
}

IStream& PrinterSettings::ReadFrom(IStream &s)
{
	char buf[100];
	Object::ReadFrom(s);
	s >> paperSize >> topLeftMargin >> bottomRightMargin >> buf >> showgrid;
	name= strsave(buf);
	return s;
}

//---- PrintManager ------------------------------------------------------------

NewMetaImpl(PrintManager,Object, (TP(currSettings), TP(printers)));

PrintManager::PrintManager()
{
	printers= new OrdCollection;

	Iter next(gClassManager->Iterator());
	Class *cla;

	while (cla= (Class*)next())
		if (cla->isKindOf(Meta(Printer)))
			printers->Add((Printer*) cla->Proto()->New());

	InstallPrinter(Env::GetValue("Printer.Default", "GENERIC"));
}

void PrintManager::InstallPrinter(const char *name)
{
	Printer *pr= FindPrinter(name);
	if (pr) {
		currPrinter= pr;
		currSettings= currPrinter->GetSettings();
		Send(cIdNone, cPartAnyChange);
	}
}

Printer *PrintManager::FindPrinter(const char *name)
{
	Iter next(printers);
	Printer *printer;

	while (printer= (Printer*)next())
		if (strcmp(name, printer->GetName()) == 0)
			return printer;
	return 0;
}

int PrintManager::PrinterId(Printer *pr)
{
	Iter next(printers);
	Printer *printer;

	for (int i= 0; printer= (Printer*)next(); i++)
		if (printer == pr)
			return i;
	return 0;
}

PrintManager::~PrintManager()
{
	if (printers) {
		printers->FreeAll();
		SafeDelete(printers);
	}
}

bool PrintManager::GetShowPageBreaks(VObject *v)
{
	return currSettings->GetShowPageBreaks()
				|| (v && v->TestFlag(eVObjShowPage));
}

void PrintManager::ShowPageGrid(Rectangle r, VObject *v)
{
	if (!gPrinting && GetShowPageBreaks(v) && GetPrinter()) {
		int i;
		Point pgs= GetViewRect().extent;
		Rectangle pgr;
		Point e= v->GetExtent();
		View *view;

		if (v->IsKindOf(View))
			view= (View*) v;
		else
			view= 0;

		GrSetPenSize(1);
		GrSetPenInk(ePatGrey50);
		//for (x= ps.x; x <= e.x; x+= ps.x)
		//    GrLine(Point(x,0), Point(x,e.y));
		int np= 1;
		for (i= pgr.origin.y= 0; pgr.origin.y <= r.origin.y+r.extent.y; pgr.origin.y+= pgr.extent.y, i++) {
			pgr.extent= pgs;
			if (view)
				pgr= view->NextPageBreak(np, pgr);
			if (i > 0)
				GrLine(Point(0, pgr.origin.y), Point(e.x, pgr.origin.y));
			np++;
		}
		GrSetPenNormal();
}
}

void PrintManager::Print(const char *name, int from, int to)
{
	bool done= FALSE;
	View *view;
	Printer *printer= GetPrinter();

	Point pgs= GetViewRect().extent;
	Rectangle pgr;
	Rectangle vr= vobject->ContentRect();

	int np= 1, pgcnt= 0;

	if (vobject->IsKindOf(View))
		view= (View*) vobject;
	else
		view= 0;

	np= 1;
	pgcnt= 0;
	for (pgr.origin.y= 0; pgr.origin.y < vr.extent.y; pgr.origin.y+= pgr.extent.y) {
		for (pgr.origin.x= 0; pgr.origin.x < vr.extent.x; pgr.origin.x+= pgr.extent.x) {
			pgr.extent= pgs;
			if (view)
				pgr= view->NextPageBreak(np, pgr);
			if (np >= from && np <= to)
				pgcnt++;
			np++;
		}
	}
	printer->Start(pgcnt);
	PrintPort *printport= printer->MakePrintPort(name);
	np= 1;
	for (pgr.origin.y= 0; !done && pgr.origin.y < vr.extent.y; pgr.origin.y+= pgr.extent.y) {
		for (pgr.origin.x= 0; pgr.origin.x < vr.extent.x; pgr.origin.x+= pgr.extent.x) {
			pgr.extent= pgs;
			if (view)
				pgr= view->NextPageBreak(np, pgr);
				
			if (np >= from && np <= to) {
				if (printer->OpenPage(np, pgs)) {
					done= TRUE;
					break;
				}

				PrintPage(np, printport, pgr);

				if (printer->ClosePage()) {
					done= TRUE;
					break;
				}
			}
			np++;
		}
	}
	SafeDelete(printport);
	printer->Finish();
}

void PrintManager::PrintPage(int np, PrintPort *printport, Rectangle pgr)
{
	View *view= 0;
	if (vobject->IsKindOf(View))
		view= (View*)vobject;

	printport->OpenPage(np);

	Port *oldport= GrGetPort();
	GrSetPort(printport);

	if (view) {
		gPrinting= TRUE;
		view->PrintAdorn(GetPaperSize(), np);
	}

	printport->ClipFurther(GetViewRect());
	printport->Translate(GetTopLeftMargin()-pgr.origin);

	vobject->DrawAll(pgr, FALSE);

	if (view)
		gPrinting= FALSE;
	GrSetPort(oldport);

	printport->ClosePage();
}

void PrintManager::ShowPrintDialog(VObject *v)
{
	if (v) {
		static PrintDialog *pd; //never delete the dialog...
		vobject= v;
		if (pd == 0)
			pd= MakePrintDialog();
		pd->ShowOnWindow(vobject->GetWindow());
	}
}

PrintDialog *PrintManager::MakePrintDialog()
{
	return new PrintDialog("Print Dialog");
}

//---- PrintDialog -------------------------------------------------------------

const int cIdSaveToFile =   cIdFirstUser + 2,
		cIdFrom       =   cIdFirstUser + 3,
		cIdTo         =   cIdFirstUser + 4,
		cIdPrint      =   cIdFirstUser + 5,
		cIdPrinters   =   cIdFirstUser + 100,
		cIdOptions    =   cIdFirstUser + 200;

NewMetaImpl(PrintDialog, Dialog, (TP(saveDialog), TP(collview), TP(from), TP(to), TP(title)));

PrintDialog::PrintDialog(char *t) : Dialog(t)
{
	title= t;
}

PrintDialog::~PrintDialog()
{
	SafeDelete(collview);
}

void PrintDialog::DoSetDefaults()
{
	from->SetValue(1);
	to->SetValue(999);
	int id= gPrintManager->PrinterId(gPrintManager->GetPrinter());
	collview->SetSelection(Point(0, id));
	options->SetValue(gPrintManager->currSettings->showgrid ? 1 : 0);
}

void PrintDialog::DoSetup()
{
	EnableItem(cIdSaveToFile, gPrintManager->GetPrinter()->CanSave());
	EnableItem(cIdPrint, gPrintManager->GetPrinter()->CanPrint());
}

void PrintDialog::StoreSettings(PrinterSettings *settings)
{
	Printer *p= gPrintManager->GetPrinter();
	settings->showgrid= options->GetValue();
	if (p && p->GetOptions())
		p->StoreSettings(settings);
	gPrintManager->Send(cIdNone, cPartAnyChange);
}

VObject *PrintDialog::DoMakeContent()
{
	collview= new ObjGridView(cIdPrinters, this, cNameMinWidth, gPrintManager->printers);

	return
		clu= new VBox(10, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVExpand),
			new Matte(
				new HBox(10, (VObjAlign)(eVObjVTop|eVObjHExpand|eVObjVExpand),
					new VBox(10, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVExpand),
						new Scroller(collview, Point(cNameMinWidth, ObjGridView::RowHeight()*4), cIdPrinters),
						new BorderItem("Options", options= new ToggleButton(cIdOptions, "Show Page Breaks")),
						new BorderItem("Pages",
							new HBox(10, eVObjVBase,
								new TextItem("Print pages from:"),
								from= new IntField(cIdFrom, 1, 999),
								new TextItem("to:"),
								to= new IntField(cIdTo, 1, 999),
								0
							)
						),
						0
					),
					new VExpander(10,
						new ActionButton(cIdApply,      "Apply Settings", TRUE),
						new ActionButton(cIdPrint,      "Print"),
						new ActionButton(cIdSaveToFile, "Print to file ..."),
						new ActionButton(cIdCancel,     "Cancel"),
						new ActionButton(cHELP,         "Help"),
						0
					),
					0
				)
			),
			0
		);
}

void PrintDialog::Control(int id, int p, void *v)
{
	if (gDebug) {
		fprintf(stderr, "PrintDialog::Control(%d, %d, 0x%08x)\n", id, p, v);
		//if (id == 5 && p == 2)
		//    abort();
	}

	switch (id) {

	case cIdFrom:
	case cIdTo:
		id= cIdNone;    // don't dismiss
		break;

	case cIdApply:
		StoreSettings(gPrintManager->currSettings);
		clu->ExtentChanged(clu);
		break;

	case cIdPrinters:
		if (p == cPartCollSelect) {
			// StoreSettings(gPrintManager->currSettings);
			gPrintManager->InstallPrinter(gPrintManager->printers->At((int)v)->AsString());
			{ // block necessary for SGI
				Printer *p= gPrintManager->GetPrinter();
				
				if (p && p->GetOptions()) {
					if (clu->Size() > 1)
						clu->SetAt(1, p->GetOptions());
					else
						clu->Add(p->GetOptions());
					p->LoadSettings(gPrintManager->currSettings);
				} else if (clu->Size() > 1)
					clu->Remove(clu->At(1));
				clu->ExtentChanged(clu);
			}
		}
		id= cIdNone;    // don't dismiss
		break;

	case cIdSaveToFile:
		if (saveDialog == 0)
			saveDialog= new FileDialog();
		if (saveDialog->ShowInWindow(eFDWrite, GetWindow()) == cIdOk) {
			clu->ExtentChanged(clu);
			StoreSettings(gPrintManager->currSettings);
			gPrintManager->Print(saveDialog->FileName(),
											from->GetValue(), to->GetValue());
		}
		id= cIdNone;    // don't dismiss
		break;
		
	case cIdPrint:
		clu->ExtentChanged(clu);
		StoreSettings(gPrintManager->currSettings);
		gPrintManager->Print(0, from->GetValue(), to->GetValue());
		id= cIdNone;    // don't dismiss
		break;
	}
	Dialog::Control(id, p, v);
}

